/**
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE and NOTICE files at the root of the source
 * tree and available online at
 *
 * http://www.dspace.org/license/
 */
package org.dspace.contentreport;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

/**
 * This class represents the complete result of a Filtered Collections report query.
 * In addition to the list of FilteredCollection entries, it contains the lazily computed
 * summary to be included in the completed report.
 *
 * @author Jean-François Morin (Université Laval)
 */
public class FilteredCollections implements Serializable {

    private static final long serialVersionUID = 3622651208704009095L;

    /** Collections included in the report */
    private List<FilteredCollection> collections = new ArrayList<>();
    /**
     * Summary generated by adding up data for each filter included in the report.
     * It will be regenerated if any non-sealed collection item is found in
     * the {@link #collections} collection attribute.
     */
    private FilteredCollection summary;

    /**
     * Shortcut method that builds a FilteredCollectionsRest instance
     * from its building blocks.
     * @param collections a list of FilteredCollectionRest instances
     * @return a FilteredCollectionsRest instance built from the provided parameters
     */
    public static FilteredCollections of(Collection<FilteredCollection> collections) {
        var colls = new FilteredCollections();
        Optional.ofNullable(collections).ifPresent(cs -> cs.stream().forEach(colls::addCollection));
        return colls;
    }

    /**
     * Returns a defensive copy of the collections included in this report.
     *
     * @return the collections included in this report
     */
    public List<FilteredCollection> getCollections() {
        return new ArrayList<>(collections);
    }

    /**
     * Adds a {@link FilteredCollectionRest} object to this report.
     *
     * @param coll {@link FilteredCollectionRest} to add to this report
     */
    public void addCollection(FilteredCollection coll) {
        summary = null;
        collections.add(coll);
    }

    /**
     * Sets all collections for this report.
     * The contents are copied into this object's internal list, which is protected against
     * further tampering with the provided list.
     *
     * @param collections Values that replace the current ones
     */
    public void setCollections(List<FilteredCollection> collections) {
        summary = null;
        this.collections.clear();
        this.collections.addAll(collections);
    }

    /**
     * Returns the report summary.
     * If the summary has not been computed yet and/or the report includes non-sealed collections,
     * it will be regenerated.
     *
     * @return the generated report summary
     */
    public FilteredCollection getSummary() {
        boolean needsRefresh = summary == null || collections.stream().anyMatch(c -> !c.getSealed());
        if (needsRefresh) {
            summary = new FilteredCollection();
            for (var coll : collections) {
                coll.getValues().forEach(summary::addValue);
            }
            int total = collections.stream()
                    .mapToInt(FilteredCollection::getTotalItems)
                    .sum();
            summary.setTotalItems(total);
            int allFilters = collections.stream()
                    .mapToInt(FilteredCollection::getAllFiltersValue)
                    .sum();
            summary.setAllFiltersValue(allFilters);
            summary.seal();
        }
        return summary;
    }

}
